App.require(["matrix4"], function (matrix4) {
    var Matrix4 = matrix4.Matrix4;
    
    function MatrixStack() {
        this.stack = [];
        this.restore();
    }
    MatrixStack.prototype = {
        constructor: MatrixStack,
        restore: function () {
            this.stack.pop();
            if (this.stack.length < 1) {
                this.stack[0] = new Matrix4();
            }
        },
        save: function () {
            this.stack.push(this.getCurrentMatrix().clone());
        },
        getCurrentMatrix: function () {
            return this.stack[this.stack.length - 1];
        },
        setCurrentMatrix: function (m) {
            return this.stack[this.stack.length - 1] = m.clone();
        },
        translate: function (x, y, z) {
            if (z === undefined) {
                z = 0;
            }
            this.getCurrentMatrix().translate(x, y, z)
            return this;
        },
        rotateZ: function (angleInRadians) {
            this.getCurrentMatrix().rotateZ(angleInRadians);
            return this;
        },
        rotateY: function (angleInRadians) {
            this.getCurrentMatrix().rotateY(angleInRadians);
            return this;
        },
        rotateX: function (angleInRadians) {
            this.getCurrentMatrix().rotateX(angleInRadians);
            return this;
        },
        rotate: function (axis, angleInRadians) {
            this.getCurrentMatrix().rotate(axis, angleInRadians);
            return this;
        },
        scale: function (x, y, z) {
            if (z === undefined) {
                z = 1;
            }
            this.getCurrentMatrix().scale(x, y, z);
            return this;
        },
        setIdentity: function () {
            this.getCurrentMatrix().identity();
            return this;
        },
        lookAt: function (eye, at, up) {
            this.getCurrentMatrix().lookAt(eye, at, up);
            return this;
        },
        perspective: function (fov, ratio, near, far) {
            Matrix4.fromPerspective(fov, ratio, near, far, this.getCurrentMatrix());
            return this;
        },
        multiply: function (matrix) {
            this.getCurrentMatrix().multiply(matrix);
            return this;
        }
    }

    App.define("matrixStack", {
        MatrixStack: MatrixStack
    })
})